package com.hertz.alarm.dao;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.transform.Transformers;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.util.CollectionUtils;

import com.hertz.alarm.util.CollectionUtil;


public class HibernateDAO implements GenericDAO {

    private HibernateTemplate hibernateTemplate;

    @Override
    public <T extends AbstractEntity<ID>, ID extends Serializable> void delete(T entity) {
        hibernateTemplate.delete(entity);
    }

    @Override
    public <T extends AbstractEntity<ID>, ID extends Serializable> void delete(ID id, Class<T> clazz) {
        T entity = hibernateTemplate.get(clazz, id);
        if (entity != null) {
            hibernateTemplate.delete(entity);
        }
    }

    @Override
    public <T extends AbstractEntity<ID>, ID extends Serializable> T get(ID id, Class<T> clazz) {
        return hibernateTemplate.get(clazz, id);
    }

    @Override
    public <T extends AbstractEntity> void merge(T entity) {
        hibernateTemplate.merge(entity);
    }

    @Override
    public <T extends AbstractEntity<ID>, ID extends Serializable> Map<ID, T> getMulti(
            final Collection<ID> ids, final Class<T> clazz) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.EMPTY_MAP;
        }
        List<T> entityList = hibernateTemplate.executeFind(new HibernateCallback<List>() {

            @Override
            public List doInHibernate(Session session)
                    throws HibernateException, SQLException {
                return session.getNamedQuery(clazz.getSimpleName() + ".byIds").
                        setParameterList("id_list", ids).list();
            }
        });
        Map<ID, T> result = new HashMap<ID, T>();
        for (T entity : entityList) {
            result.put(entity.getId(), entity);
        }
//        Map<ID, T> result = new HashMap<ID, T>();
//        for (ID id : ids) {
//            result.put(id, hibernateTemplate.get(clazz, id));
//        }
        return result;
    }
    
    @Override
    public <T> T getUniqueResult(
            final String name, final Object... params) {
        return hibernateTemplate.execute(new HibernateCallback<T>() {

            @Override
            public T doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                for (int i = 0; i < params.length; i++) {
                    query.setParameter(i, params[i]);
                }
                return (T) query.uniqueResult();
            }
        });
    }
    
    @Override
    public <T> T getUniqueResultByParamMap(
            final String name,final Map<String, Object> paramMap) {
        T result = hibernateTemplate.execute(new HibernateCallback<T>() {

            @Override
            public T doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                if (paramMap != null){
                	Set<String> keySet = paramMap.keySet();
                	for(String key : keySet){
                		if(paramMap.get(key) instanceof Collection){
                			query.setParameterList(key, (Collection)paramMap.get(key));
                		}else{
                			query.setParameter(key, paramMap.get(key));
                		}
                	}
                }
                return (T) query.uniqueResult();
            }
        });
        return result;
    }

    @Override
    public <T extends AbstractEntity> void save(T entity) {
        hibernateTemplate.save(entity);
    }

    @Override
    public <T extends AbstractEntity> void saveOrUpdate(T entity) {
        hibernateTemplate.saveOrUpdate(entity);
    }

    @Override
    public <T extends AbstractEntity> void saveOrUpdateAll(Collection<T> entities) {
        hibernateTemplate.saveOrUpdateAll(entities);
    }

    @Override
    public <T extends AbstractEntity> void update(T entity) {
        hibernateTemplate.update(entity);
    }

    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }

    @Override
    public <T> List<T> listAll(
            final String name, final Object... params) {
        return hibernateTemplate.executeFind(new HibernateCallback<List>() {

            @Override
            public List doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                if(params != null)
	                for (int i = 0; i < params.length; i++) {
	                    query.setParameter(i, params[i]);
	                }
                return query.list();
            }
        });
    }

    @Override
    public <T> List<T> listByParams(
            final String name,final int firstResult, final int maxResults, final Object... params) {
        return hibernateTemplate.executeFind(new HibernateCallback<List>() {

            @Override
            public List doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                for (int i = 0; i < params.length; i++) {
                    query.setParameter(i, params[i]);
                }
                query.setFirstResult(firstResult);
                query.setMaxResults(maxResults);
                return query.list();
            }
        });
    }
    @Override
    public <T> List<T> listByParamsToMap(
            final String name,final int firstResult, final int maxResults, final Object... params) {
        return hibernateTemplate.executeFind(new HibernateCallback<List>() {

            @Override
            public List doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                for (int i = 0; i < params.length; i++) {
                    query.setParameter(i, params[i]);
                }
                query.setFirstResult(firstResult);
                query.setMaxResults(maxResults);
                query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
                return query.list();
            }
        });
    }
    
    @Override
    public <T> List<T> listByParamMap(final String name,final int firstResult,final int maxResults, final Map<String, Object> paramMap){
    	return hibernateTemplate.executeFind(new HibernateCallback<List>() {

            @Override
            public List<T> doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                if (paramMap != null){
                	Set<String> keySet = paramMap.keySet();
                	for(String key : keySet){
                		if(paramMap.get(key) instanceof Collection){
                			query.setParameterList(key, (Collection)paramMap.get(key));
                		}else{
                			query.setParameter(key, paramMap.get(key));
                		}
                	}
                }
                query.setFirstResult(firstResult);
                query.setMaxResults(maxResults);
                return query.list();
            }
        });
    }
    
    @Override
    public <T extends AbstractEntity<ID>, ID extends Serializable> List<T> toEntityList(List<ID> idList, Class<T> clazz) {
        Map<ID, T> map = getMulti(idList, clazz);
        return CollectionUtil.toList(map, idList);
    }
    
    @Override
    public Integer executeUpdate(
            final String name, final Object... params) {
        return hibernateTemplate.execute(new HibernateCallback<Integer>() {

            @Override
            public Integer doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                for (int i = 0; i < params.length; i++) {
                    query.setParameter(i, params[i]);
                }
                return query.executeUpdate();
            }
        });
    }

	@Override
	public <T> List<T> listAll(Class<T> clazz) {
		return hibernateTemplate.loadAll(clazz);
	}
    
    
}
